use crate::config::app_error::AppResult;
use crate::extension::index::index::query::index_entry::IndexEntry;
use halo_model::{ExtensionGVK, ExtensionOperator, GVK};
use std::{cell::RefCell, collections::VecDeque, sync::Arc};

pub struct IndexerTransaction<E>
where
    E: IndexEntry,
    E::T: ExtensionGVK,
{
    in_transaction: bool,
    change_records: VecDeque<ChangeRecord<E>>,
}

impl<E> IndexerTransaction<E>
where
    E: IndexEntry,
    E::T: ExtensionGVK,
{
    pub fn new() -> Self {
        IndexerTransaction {
            in_transaction: false,
            change_records: VecDeque::new(),
        }
    }
    pub fn begin(&mut self) -> AppResult<()> {
        if self.in_transaction {
            return Err(anyhow::anyhow!("Transaction already active").into());
        }
        self.change_records = VecDeque::new();
        self.in_transaction = true;
        Ok(())
    }

    pub fn commit(&mut self) -> AppResult<()> {
        if !self.in_transaction {
            return Err(anyhow::anyhow!("Transaction not started").into());
        }

        let mut commited_records = VecDeque::new();
        while let Some(change_record) = self.change_records.pop_front() {
            match Self::apply_change(&change_record) {
                Ok(_) => {}
                Err(e) => {
                    while let Some(change_record) = commited_records.pop_front() {
                        Self::revert_change(&change_record)?;
                    }
                    return Err(e);
                }
            };
            commited_records.push_back(change_record);
        }
        self.in_transaction = false;

        Ok(())
    }

    pub fn rollback(&mut self) -> AppResult<()> {
        Self::check_thread();
        if !self.in_transaction {
            return Err(anyhow::anyhow!("Transaction not started").into());
        }
        self.change_records.clear();
        self.in_transaction = false;
        Ok(())
    }

    pub fn add(&mut self, change_record: ChangeRecord<E>) -> AppResult<()> {
        if self.in_transaction {
            self.change_records.push_front(change_record);
        } else {
            return Err(anyhow::anyhow!("No active transaction to add changes").into());
        }
        Ok(())
    }

    fn check_thread() {}

    fn apply_change(change_record: &ChangeRecord<E>) -> AppResult<()> {
        let index_entry = &change_record.index_entry;
        if change_record.is_add {
            index_entry
                .borrow_mut()
                .add_entry(vec![change_record.key.clone()], change_record.value.clone())?;
        } else {
            index_entry
                .borrow_mut()
                .remove_entry(change_record.key.clone(), change_record.value.clone());
        }
        Ok(())
    }

    fn revert_change(change_record: &ChangeRecord<E>) -> AppResult<()> {
        let index_entry = &change_record.index_entry;
        if change_record.is_add {
            index_entry
                .borrow_mut()
                .remove_entry(change_record.key.clone(), change_record.value.clone());
        } else {
            index_entry
                .borrow_mut()
                .add_entry(vec![change_record.key.clone()], change_record.value.clone())?;
        }
        Ok(())
    }
}

pub struct ChangeRecord<E>
where
    E: IndexEntry,
    E::T: ExtensionGVK,
{
    pub index_entry: Arc<RefCell<E>>,
    pub key: String,
    pub value: String,
    pub is_add: bool,
}

impl<E> ChangeRecord<E>
where
    E: IndexEntry,
    E::T: ExtensionGVK,
{
    pub fn on_add(index_entry: Arc<RefCell<E>>, key: String, value: String) -> Self {
        ChangeRecord {
            index_entry,
            key,
            value,
            is_add: true,
        }
    }

    pub fn on_remove(index_entry: Arc<RefCell<E>>, key: String, value: String) -> Self {
        ChangeRecord {
            index_entry,
            key,
            value,
            is_add: false,
        }
    }
}
